package com.congwulin.wechat.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 类属性查找器
 */
public class ClassFieldFinder {

    /**
     * 根据Class类型获取属性列表
     *
     * @param clazz Class类型
     * @return 属性列表
     */
    public Map<String, ClassField> getAttributes(Class<?> clazz) {
        Map<String, ClassField> attributeIndex = new HashMap<String, ClassField>();
        List<Field> fieldList = this.getFields(clazz);
        Map<String, Method> index = this.getMethods(clazz);
        for (Field field : fieldList) {
            String name = this.getGetterName(field);
            Method getter = index.get(name);
            name = this.getSetterName(field);
            Method setter = index.get(name);
            if (this.isAttribute(getter, setter)) {
                ClassField attribute = new ClassField(field, getter, setter);
                attributeIndex.put(field.getName(), attribute);
            }
        }
        return attributeIndex;
    }

    private List<Field> getFields(Class<?> clazz) {
        List<Field> list = new ArrayList<Field>();
        this.getFields(clazz, list);
        return list;
    }

    private void getFields(Class<?> clazz, List<Field> list) {
        Class<?> father = clazz.getSuperclass();
        if (father != null && father != Object.class) {
            this.getFields(father, list);
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            list.add(field);
        }
    }

    private String getGetterName(Field field) {
        StringBuilder sb = new StringBuilder();
        String name = field.getName();
        sb.append("get");
        sb.append(name.substring(0, 1).toUpperCase());
        sb.append(name.substring(1));
        return sb.toString();
    }

    private Map<String, Method> getMethods(Class<?> clazz) {
        Map<String, Method> index = new HashMap<String, Method>();
        this.getMethods(clazz, index);
        return index;
    }

    private void getMethods(Class<?> clazz, Map<String, Method> index) {
        Class<?> father = clazz.getSuperclass();
        if (father != null && father != Object.class) {
            this.getMethods(father, index);
        }
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            index.put(method.getName(), method);
        }
    }

    private String getSetterName(Field field) {
        StringBuilder sb = new StringBuilder();
        String name = field.getName();
        sb.append("set");
        sb.append(name.substring(0, 1).toUpperCase());
        sb.append(name.substring(1));
        return sb.toString();
    }

    private boolean isAttribute(Method getter, Method setter) {
        if (getter == null) {
            return false;
        }
        boolean flag = getter.getModifiers() == Modifier.PUBLIC;
        if (setter != null) {
            flag = flag && setter.getModifiers() == Modifier.PUBLIC;
        }
        // 至少有public的公共方法才能称为属性
        return flag;
    }
}
